<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <script src="https://d3js.org/d3.v4.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/dat-gui/0.5/dat.gui.min.js"></script>
    <title>deltaOverall--GUAN's Lab</title>
</head>
<body>
    <div class="panel">
        <div id="title">Counter:</div>
        <div id="counter"></div>
    </div>
    <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
	width="300" height="150" viewBox="0 0 1920 1080" class="logo">
<g id="图层_2">
	<g>
		<g>
			<path class="st0" d="M1388.5,216.68c1.26-0.82,2.77-1.31,4.39-1.31h67.67c1.62,0,3.13,0.48,4.39,1.31l75.77-75.77
				c-29.35-29-69.68-46.91-114.2-46.91c-44.41,0-84.65,17.82-113.98,46.7L1388.5,216.68z"/>
		</g>
		<g>
			<path class="st1" d="M1543.37,143.6l-75.83,75.83c0.67,1.17,1.05,2.52,1.05,3.97v67.67c0,1.62-0.48,3.12-1.31,4.39l75.02,75.02
				c28.87-29.33,46.7-69.57,46.7-113.98C1589,212.64,1571.61,172.84,1543.37,143.6z"/>
		</g>
		<g>
			<path class="st2" d="M1464.52,298.06c-1.17,0.67-2.52,1.05-3.97,1.05h-67.67c-1.45,0-2.8-0.39-3.97-1.05l-75.31,75.31
				c29.24,28.25,69.03,45.63,112.9,45.63c43.97,0,83.86-17.47,113.11-45.85L1464.52,298.06z"/>
		</g>
		<g>
			<path class="st3" d="M1386.15,295.46c-0.82-1.26-1.31-2.77-1.31-4.39v-67.67c0-1.45,0.39-2.8,1.05-3.97l-76.05-76.05
				c-28.37,29.26-45.85,69.14-45.85,113.11c0,44.52,17.91,84.85,46.91,114.2L1386.15,295.46z"/>
		</g>
	</g>
	<g>
		<rect x="202.54" y="357.72" class="st4" width="1122.46" height="493.38"/>
		<text transform="matrix(1 0 0 1 202.5396 683.8184)"><tspan x="0" y="0" class="st3 st5 st6">G</tspan><tspan x="295.58" y="0" class="st2 st5 st6">U</tspan><tspan x="570" y="0" class="st0 st5 st6">A</tspan><tspan x="823.46" y="0" class="st1 st5 st6">N</tspan></text>
	</g>
</g>
<g id="图层_3">
	<rect x="348.06" y="737.15" class="st4" width="830.94" height="74.85"/>
	<text transform="matrix(1 0 0 1 348.0562 784.3447)" class="st7 st5 st8 st9">SYSTEM LABORATORY</text>
</g>
</svg>
    <script>
        //TODO:
        /* 变量的声明：
        dataOriginExample原数组，dataOrigin中间数组，dataOutput最终格式化的数组用于构造位置信息
        火柴的颜色比例尺：colorMatchASC增长 colorMAtchDESC下降
        delta增长的数组asc，delta降低的数组desc
         */
         /* 
         主要函数的声明
         format() 返回dataOutput
          */

        //定义一下数据源数据
        const dataOriginExample = {
            axisnode: [
                //后期用d3.range生成
            ],
            link: [
                {
                    name: 'zhangsan',
                    start: 90,
                    end: 110
                },
                {
                    name: 'lisi',
                    start: 90,
                    end: 110
                },
                {
                    name: 'wangwu',
                    start: 90,
                    end: 110
                },
                {
                    name: 'zhaoliu',
                    start: 90,
                    end: 110
                },
                {
                    name: 'ruanqi',
                    start: 90,
                    end: 110
                },
                {
                    name: 'laoba',
                    start: 90,
                    end: 110
                },
                {
                    name: 'qinjiu',
                    start: 90,
                    end: 110
                },
                {
                    name: 'xiaoshi',
                    start: 90,
                    end: 110
                },
                {
                    name: 'woshiele',
                    start: 90,
                    end: 110
                },
                {
                    name: 'iamtwel',
                    start: 90,
                    end: 110
                },
                {
                    name: 'sheishithirt',
                    start: 90,
                    end: 110
                },
                {
                    name: 'iamlast',
                    start: 90,
                    end: 110
                },
            ]
        }
        
        let shuffleFrom = d3.shuffle(d3.range(75, 150))
        let shuffleTo = d3.shuffle(d3.range(75, 150))

        //自定义demo的原始数组
        let dataOrigin = {}
        dataOrigin.axisnode = d3.range(151);
        
        dataOrigin.link = d3.range(50).map((d, i)=>{
            return {
                name: Math.random().toString(36).slice(-6),
                start: shuffleFrom[i],
                end: shuffleTo[i]
            }
        })//到此源数据dataOrigin就已经被定义成功了，这就是假定的虚拟数据源

        console.log('dataOrigin', dataOrigin)

       
        //预设一个GUI
        let controls = new function(){
            this.innerRadius = 175
            this.strokeWidth = 5
            this.colorAsc = [16, 128, 35, 0.932]
            this.colorDesc = [168, 39, 16, 0.932]
        }



        //定义一些变量
        const width = 960,height = 600,
        outerRadius = 250,innerRadius = 175,
        textBias = 15

        //定义环形的映射比例尺
        let sqrt = d3.scalePow()
        .exponent(2.5)
        .domain([150, 0])
        .range([0, Math.PI]);
        // let sqrt = d3.scaleLinear()
        // .domain()
        // .range([0, Math.PI])

        let dataOutput = format(dataOrigin)
        asc = [], desc = []

        dataOutput.link.forEach(d=>{
            if(d.delta > 0) asc.push(d.delta);
            else desc.push(d.delta)
        })

        let colorAsc = 'rgba('.concat(controls.colorAsc.join(',')).concat(')')
        let colorAsc0 = 'rgba('+controls.colorAsc[0]+','+controls.colorAsc[1]+','+controls.colorAsc[2]+','+0.4+')'
        let colorDesc = 'rgba('.concat(controls.colorDesc.join(',')).concat(')')
        let colorDesc0 = 'rgba('+controls.colorDesc[0]+','+controls.colorDesc[1]+','+controls.colorDesc[2]+','+0.4+')'

        let colorMatchASC = d3.scaleSequential(d3.interpolateLab(colorAsc0, colorAsc))
        .domain(d3.extent(asc));
        let colorMatchDESC = d3.scaleSequential(d3.interpolateLab(colorDesc, colorDesc0))
        .domain(d3.extent(desc));


        
        
        //计算交点函数
        
        // function equation(r, x1, y1, x2, y2){
            // let x = new Expression('x')

            // let y = new Expression('y')
            // // let t1 =   algebra.parse(`x-${x1}`).multiply(algebra.parse(`${y2-y1}`))
            // let eq = new Equation(algebra.parse(`y-${y1}`).multiply(algebra.parse(`${x2-x1}`)), algebra.parse(`x-${x1}`).multiply(algebra.parse(`${y2-y1}`)))
            // let y0 = eq.solveFor('y')

            // let eqn = new Equation(x.multiply(x).add(algebra.parse(`${y0}`).multiply(algebra.parse(`${y0}`))),radius*radius)
            // let solution = eqn.solveFor('x')
        //     let sol1 = nerdamer.solve(`${x2-x1}*(y-${y1}) = ${y2-y1}*(x-${x1})`,'y').toString().slice(1,-1)
        //     let solution = nerdamer.solve(`x^2+(${sol1})^2=${r}^2`,'x').toString().slice(1,-1).split(',')
        //     return solution.every((item)=>item<r&&item>0)&&solution.length>1&&solution[0]!==solution[1];
        // }
        
        //构造数学公式通过求公共解来计算交点的个数

        // let unionCounter = dataOutput.link.reduce((prev,cur,i)=>{
        //     if(equation(controls.innerRadius, outerRadius*Math.cos(dataOutput.axisnode[cur.fromId].angle), outerRadius*Math.sin(dataOutput.axisnode[cur.fromId].angle), controls.innerRadius*Math.cos(dataOutput.axisnode[cur.toId].angle), controls.innerRadius*Math.sin(dataOutput.axisnode[cur.toId].angle))){
        //         return prev+1;
        //     }else{
        //         return prev;
        //     }
        // },0)

        // let unionCounter = 0
        // dataOutput.link.forEach(cur=>{
        //     if(equation(controls.innerRadius, outerRadius*Math.cos(dataOutput.node[cur.from].angle), outerRadius*Math.sin(dataOutput.node[cur.from].angle), controls.innerRadius*Math.cos(dataOutput.node[cur.to].angle), controls.innerRadius*Math.sin(dataOutput.node[cur.to].angle))){
        //         unionCounter++
        //     }
        // })

        // console.log('   unionCounter', unionCounter)

        //定义画布
        const svg = d3.select('body')
        .append('svg')
        .attr('width', width)
        .attr('height', height)
        .append('g')
        .attr('transform', `translate(${width/2},${height/2})`)


        //画放射的标识虚线
        let radialLine = d3.radialLine()
        .angle(d=>d.angle)
        .radius(d=>d.radius)

        let strokeDataForPolarAxis = dataOutput.axisnode.map((d,i)=>{
            return d.tick%20 == 0 ||d.tick == 0 ||d.tick == 150?
            [
                {
                    angle: 0,
                    radius: 0
                },
                {
                    angle: d.angle,
                    radius: outerRadius
                }
            ]:[ ];
        })

        svg.append('g')
        .selectAll('.radial-stroke')
        .data(strokeDataForPolarAxis)
        .enter()
        .append('path')
        .attr('class', 'radial-stroke')
        .attr('d', radialLine)

        //画outer环形坐标的圆
        svg.append('g')
        .attr('class', 'axis-outer')
        .append('path')
        .attr('d', d3.arc()
        .innerRadius(outerRadius)
        .outerRadius(outerRadius)
        .startAngle(0)
        .endAngle(Math.PI)
        )

        //画inner环形坐标的圆
        let innerArc = d3.arc()
        .innerRadius(innerRadius)
        .outerRadius(innerRadius)
        .startAngle(0)
        .endAngle(Math.PI)

        let innerCircle = svg.append('g')
        .attr('class', 'axis-inner')
        .append('path')
        .attr('d', innerArc)

        //绘制inner坐标上的刻度
        let xAxisInner = svg.append("g");
  
        var xTick = xAxisInner
            .selectAll("g")
            .data(sqrt.ticks(30))
            .enter().append("g")
            .attr("text-anchor", "middle")
            .attr("transform", function(d) {
                return "rotate(" + ((sqrt(d)) * 180 / Math.PI - 90) + ")translate(" + innerRadius + ",0)";
            });
        
        xTick.append("line")
            .attr("x2", (d,i)=>{
                return d%20 == 0 ||d == 0 ||d == 150? 7:4;
            })
            .attr("stroke", "rgb(82, 79, 79)")
            .attr('stroke-width', d=>{
                return d%20 == 0 ||d == 0 ||d == 150? 2:1;
            })

        // //绘制outer坐标上的刻度
        let xAxisOuter = svg.append("g");

        var xOuterTick = xAxisOuter
            .selectAll("g")
            .data(sqrt.ticks(30))
            .enter().append("g")
            .attr("text-anchor", "middle")
            .attr("transform", function(d) {
                return "rotate(" + ((sqrt(d)) * 180 / Math.PI - 90) + ")translate(" + outerRadius + ",0)";
            });
        
            xOuterTick.append("line")
            .attr("x2", (d,i)=>{
                return d%20 == 0 ||d == 0 ||d == 150? 7:4;
            })
            .attr("stroke", "rgb(82, 79, 79)")
            .attr('stroke-width', d=>{
                return d%20 == 0 ||d == 0 ||d == 150? 2:1;
            });

            xOuterTick.append("text")
            .attr("transform", function(d) { 
            var angle = sqrt(d);
            return ((angle < Math.PI / 2) || (angle > (Math.PI * 3 / 2))) ? "rotate(90)translate(0,-14)" : "rotate(-90)translate(0, 18)"; })
            .text(function(d) { 
                return d%20 == 0 ||d == 0 ||d == 150? d:null;
            })
            .style("font-size", 12)
            .style('font-weight', 500)


        //定义tooltip的selection
        let div = d3.select('body')
        .append('div')
        .attr('class', 'tooltip')
        .style('opacity', 0);

        //计算切线长度，来判断线段的交点个数
        let tangent = Math.sqrt(outerRadius*outerRadius-controls.innerRadius*controls.innerRadius)
        let intersectCounter = 0

        //绘制axis之间的link
        let dataForRadialLine = dataOutput.link.map((d,i)=>{
            return [{
                uid: d.uid,
                name: d.name,
                fromId: d.fromId,
                toId: d.toId,
                fromGrade: d.fromGrade,
                toGrade: d.toGrade,
                delta: d.delta,
                angle:dataOutput.axisnode[d.fromId].angle,
                radius: outerRadius
                },{
                // uid: d.uid,
                // name: d.name,
                // fromId: d.fromId,
                // toId: d.toId,
                // fromGrade: d.fromGrade,
                // toGrade: d.toGrade,
                // delta: d.delta,
                angle:dataOutput.axisnode[d.toId].angle,
                radius: innerRadius
            }]
        })
        let link = svg.append('g')
        .selectAll('.link')
        .data(dataForRadialLine)
        .enter()
        .append('path')
        // .each(function(d){d.source = d[0],d.target = d[1]})
        .attr('class', 'link')
        .attr('d', radialLine)
        .attr('stroke', d=>{
            return d[0].delta>0?colorMatchASC(d[0].delta):colorMatchDESC(d[0].delta)
        })
        .each(d=>{
            if(Math.sqrt(Math.pow(d[0].radius,2)+(Math.pow(d[1].radius,2))-2*d[0].radius*d[1].radius*Math.cos(d[0].angle-d[1].angle))>tangent){
                intersectCounter++
            }
        })
        .on('mouseover', mouseover)
        .on("mouseout", mouseout)

        document.getElementById('counter').innerHTML =intersectCounter


        //画D区域的link
        let clip = svg.append('defs')
        .append('clipPath')
        .attr('id', 'clip')
        .append('circle')
        .attr('r', innerRadius)

        let link_d = svg.append('g')
        .attr('clip-path', 'url(#clip)')
        .selectAll('.link-d')
        .data(dataForRadialLine)
        .enter()
        .append('path')
        // .each(function(d){d.source = d[0],d.target = d[1]})
        .attr('class', 'link-d')
        .attr('d', radialLine)
        .attr('stroke', d=>{
            return d[0].delta>0?colorMatchASC(d[0].delta):colorMatchDESC(d[0].delta)
        })
        .attr('stroke-width', 5)
        .on('mouseover', mouseover_d)
        .on("mouseout", mouseout_d)



        //配置gui
        let gui = new dat.GUI()

        gui.add(controls, 'innerRadius', controls.innerRadius-60, outerRadius).name('Sensitivity').step(2).onChange(innerRadius=>{
            let tangent = Math.sqrt(outerRadius*outerRadius-innerRadius*innerRadius)
            let intersectCounter = 0
            //半圆
            innerArc.innerRadius(innerRadius)
            .outerRadius(innerRadius)

            innerCircle.attr('d', innerArc)
            //刻度
            xTick.attr("transform", function(d) {
                return "rotate(" + ((sqrt(d)) * 180 / Math.PI - 90) + ")translate(" + innerRadius + ",0)";
            })
            //layer之间的link
            dataForRadialLine.forEach(item=>{
                item[1].radius = innerRadius
            })
            link.remove()
            link = svg.append('g')
            .selectAll('.link')
            .data(dataForRadialLine)
            .enter()
            .append('path')
            .attr('class', 'link')
            .attr('d', radialLine)
            .each(d=>{
                if(Math.sqrt(Math.pow(d[0].radius,2)+(Math.pow(d[1].radius,2))-2*d[0].radius*d[1].radius*Math.cos(d[0].angle-d[1].angle))>tangent){
                    intersectCounter++
                }
            })
            .attr('stroke', d=>{
                return d[0].delta>0?colorMatchASC(d[0].delta):colorMatchDESC(d[0].delta)
            })
            .on('mouseover', mouseover)
            .on("mouseout", mouseout)

        document.getElementById('counter').innerHTML =intersectCounter

            //d区域的link
            clip
            .attr('r', innerRadius)

            link_d.remove()
            link_d = svg.append('g')
            .attr('clip-path', `url(#clip)`)
            .selectAll('.link-d')
            .data(dataForRadialLine)
            .enter()
            .append('path')
            .attr('class', 'link-d')
            .attr('d', radialLine)
            .attr('stroke', d=>{
                return d[0].delta>0?colorMatchASC(d[0].delta):colorMatchDESC(d[0].delta)
            })
            .attr('stroke-width', controls.strokeWidth)
            // .attr('opacity', 0.1)
            // .style('opacity', 0.01)
            .on('mouseover', mouseover)
            .on("mouseout", mouseout)
        })
        gui.add(controls, 'strokeWidth', 2.5, 15).name('Filering Width').onChange(strokeWidth=>{
            link_d.style('stroke-width', strokeWidth)
        })
        gui.addColor(controls, 'colorAsc').name('Color of Asc').onChange(e=>{
            //修改上升的
            e = round(e)
            colorAsc = 'rgba('.concat(e.join(',')).concat(')')
            colorAsc0 = 'rgba('+e[0]+','+e[1]+','+e[2]+','+0.4+')'
            colorMatchASC = d3.scaleSequential(d3.interpolateLab(colorAsc0, colorAsc))
            .domain(d3.extent(asc));
            link_d.attr('stroke', d=>{
                return d[0].delta>0?colorMatchASC(d[0].delta):colorMatchDESC(d[0].delta)
            })
            link.attr('stroke', d=>{
                return d[0].delta>0?colorMatchASC(d[0].delta):colorMatchDESC(d[0].delta)
            })
        })
        gui.addColor(controls, 'colorDesc').name('Color of Desc').onChange(e=>{
            //修改下降的
            e = round(e)
            colorDesc = 'rgba('.concat(e.join(',')).concat(')')
            colorDesc0 = 'rgba('+e[0]+','+e[1]+','+e[2]+','+0.4+')'
            colorMatchDESC = d3.scaleSequential(d3.interpolateLab(colorDesc, colorDesc0))
            .domain(d3.extent(desc));

            link_d.attr('stroke', d=>{
                return d[0].delta>0?colorMatchASC(d[0].delta):colorMatchDESC(d[0].delta)
            })
            link.attr('stroke', d=>{
                return d[0].delta>0?colorMatchASC(d[0].delta):colorMatchDESC(d[0].delta)
            })
        })


        //交互：tooltip  mouseover
        function mouseover(d,i){
            div.transition()
            .duration(170)
            .style('opacity', 0.9);
            div.html(`Name: ${d[0].name}</br>Period One: ${d[0].fromGrade}</br>Period Two: ${d[0].toGrade}</br>delta: ${d[0].delta}`)
            .style('left', (d3.event.pageX) + "px")
            .style("top", (d3.event.pageY - 28) + "px");
            link_d.style('display', 'none')
            
            // link_d.classed('link-d-hover',(item,i,nodes)=>{
            //     return d.name !== item.name
            // })
            // link.classed('link-hover',   (item,i,nodes)=>{
            //     return d.name !== item.name
            // })
        }

        function mouseout(d,i){
            div.transition()
            .duration(500)
            .style("opacity", 0);
            link_d.style('display', 'block')
        }

        function mouseover_d(d,i){
            div.transition()
            .duration(170)
            .style('opacity', 0.9);
            div.html(`Name: ${d[0].name}</br>Period One: ${d[0].fromGrade}</br>Period Two: ${d[0].toGrade}</br>delta: ${d[0].delta}`)
            .style('left', (d3.event.pageX) + "px")
            .style("top", (d3.event.pageY - 28) + "px");
            link_d.style('display', 'none')
        }

        function mouseout_d(d,i){
            div.transition()
            .duration(500)
            .style("opacity", 0);
            link_d.style('display', 'block')
        }


        //对数组的元素进行四舍五入的函数
        function round(arr){
            return arr.map(d=>{
                return Math.round(d)
            })
        }
        //定义格式化函数
        function format(example){
            let data ={}

            data.axisnode = example.axisnode.map((d, i)=>{
                return {
                    uid: i,
                    tick: d,
                    angle: sqrt(d)
                }
            })

            let fromArray = [], toArray = []
            data.link = example.link.map((d, i)=>{
                let from = d.start, to = d.end,


                fromArrayEle = data.axisnode.findIndex((item)=>{
                    return item.tick == from
                })
                fromArray.push(fromArrayEle)

                toArrayEle = data.axisnode.findIndex((item)=>{
                    return item.tick == to
                })
                toArray.push(toArrayEle)

                return {
                    uid: i,
                    name: d.name,
                    fromId: fromArray[i],
                    toId: toArray[i],
                    fromGrade: d.start,
                    toGrade: d.end,
                    delta: d.end - d.start
                }
            })
            
            return data;
        }

        console.log('dataOutput', dataOutput)
    </script>
</body>
<style>
.axis-outer{
    stroke: rgb(82, 79, 79);
    stroke-width: 2px;
}
.axis-inner{
    stroke: rgb(82, 79, 79);
    stroke-width: 1.5px;
}
.link{
    stroke-opacity: 0.5;
    stroke-width: 2.5px;
    stroke-linecap: round;
    transition: all 0.3s ease;

}
.link-d{
    /* stroke-width: 5; */
    /* transition: all 0.3s ease; */
    stroke-linecap: round;

}
.radial-stroke{
    stroke: gray;
    stroke-dasharray: 4px;
    stroke-width: 1px;
    opacity: 0.5;
}
.tooltip{
    position: absolute; 
    padding: 10px;
    font: 12px sans-serif;
    background: lightsteelblue;
    border: 0px;
    border-radius: 8px;
    pointer-events: none;
    text-align: left;
    line-height: 1.5em;
    font-weight: bold;
}
.link:hover{
    stroke-width: 6px;
    stroke-opacity: 1;
}
/* 修改gui的样式 */
.dg .c .slider-fg {
    background: #d6642f;
}
.dg .cr.number input[type=text] {
    color: #d6642f;
}
.panel{
    position: absolute;
    right: 0;
    top:180px;
    min-width:300px;
    text-align: center;
    font: 4em  "Helvetica Neue", Helvetica, Arial, sans-serif;
    color: rgb(139, 139, 139);
    line-height: 1.5em;
}
#counter{
    font-size: 2.4em;
}
.st0{fill:#FFBC4A;}
.st1{fill:#31B729;}
.st2{fill:#FF5D5B;}
.st3{fill:#5171FE;}
.st4{fill:none;}
.st5{font-family:'ArialMT';}
.st6{font-size:380px;}
.st7{fill:#BFBFBF;}
.st8{font-size:55px;}
.st9{letter-spacing:12;}
.logo{
    position: absolute;
    bottom: 0;
    left:0;
}
</style>
</html>